﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace TotalFreedomKO
{   
    public interface IKoAttributeHtmlString<TModel> : IHtmlString
    {
        bool HasKoAttribute(string attributeName);
        bool IsKoAttributeSupported(string attributeName);
        bool HasKoElement();
        List<KeyValuePair<string, string>> GetAttributeValues();
        IKoElementHtmlString<TModel> GetKoElement();
    }

    public class KoAttributeHtmlString2<TModel, TProperty> : KoAttributeHtmlString<TModel, TProperty>
    {
        public TProperty Value { get; set; }

        public KoAttributeHtmlString2(IKoAttributeHtmlString<TModel> parent, string attributeName, string attributeValue, ViewDataDictionary<TModel> viewData = null, string[] supportedAttributes = null)
            : base(parent, attributeName, attributeValue, viewData, supportedAttributes)
        {
        }
    }

    public class KoAttributeHtmlString3<TModel, TProperty> : IHtmlString, IKoHelper<TProperty>
    {
        private KoAttributeHtmlString<TModel, TProperty> _htmlString;

        public HtmlHelper HtmlHelper
        {
            get;
            private set;
        }

        public KoAttributeHtmlString3(HtmlHelper htmlHelper, IKoAttributeHtmlString<TModel> parent, string attributeName, string attributeValue, string[] supportedAttributes = null)
        {
            _htmlString = new KoAttributeHtmlString<TModel, TProperty>(null, attributeName, attributeValue, null, supportedAttributes);
            HtmlHelper = htmlHelper;
        }

        public virtual string ToHtmlString()
        {
            return _htmlString.ToHtmlString();

        }
    }

    public class KoAttributeHtmlString<TModel, TProperty> : IHtmlString, IKoAttributeHtmlString<TModel>
    {
        // list of knockout attributes that allow values in binding
        private static string[] KO_ATTR_MULTIPLE_VALUES = { "css", "style", "attr" };

        internal readonly string _attributeValue;
        private readonly string _attributeName;
        private readonly string[] _supportedAttributes;
        private readonly IKoAttributeHtmlString<TModel> _parent;

        public KoAttributeHtmlString(IKoAttributeHtmlString<TModel> parent, string attributeName, string attributeValue, object viewData = null, string[] supportedAttributes = null)
            : base()
        {
            if(String.IsNullOrWhiteSpace(attributeName))
                throw(new Exception("Attribute name must be non-empty"));

            _attributeValue = attributeValue ?? String.Empty;
            _attributeName = attributeName;
            _parent = parent;
            _supportedAttributes = supportedAttributes;

            // validate that no parents already have such attribute
            if (_parent != null)
            {
                if (_parent.HasKoAttribute(attributeName) && !KO_ATTR_MULTIPLE_VALUES.Contains(attributeName.ToLower()) )
                    throw (new ArgumentException(String.Format("The KO chain already has attribute {0}", attributeName)));

                if (!_parent.IsKoAttributeSupported(attributeName))
                    throw (new ArgumentException(String.Format("The KO attribute {0} is not supported", attributeName)));
            }
        }

        public virtual bool HasKoAttribute(string attributeName)
        {
            if (_attributeName == attributeName)
                return true;

            if (_parent != null)
                return _parent.HasKoAttribute(attributeName);

            return false;
        }

        public static bool IsNullOrEmpty(KoAttributeHtmlString<TModel, TProperty> value)
        {
            return (value == null || value._attributeValue.Length == 0);
        }

        public virtual string ToHtmlString()
        {
            string d = "";
            string attributeValues = "";
            Dictionary<string, string> multipleValues = new Dictionary<string, string>();

            if (_parent != null)
            {
                // if has an element in the list, let it handle it
                if (_parent.HasKoElement())
                    return _parent.GetKoElement().ToHtmlString(this);

                List<KeyValuePair<string, string>> values = _parent.GetAttributeValues();
                if (values != null)
                {
                    foreach (KeyValuePair<string, string> value in values)
                    {
                        string lowerKey = value.Key.ToLower();
                        if (KO_ATTR_MULTIPLE_VALUES.Contains(lowerKey))
                        {
                            if(multipleValues.ContainsKey(lowerKey))
                                multipleValues[lowerKey] = multipleValues[lowerKey] + ", " + value.Value;
                            else
                                multipleValues[lowerKey] = value.Value;
                        }
                        else
                            attributeValues += d + value.Value;

                        d = ", ";
                    }
                }
            }

            string s;
           
            string lowerAttr = _attributeName.ToLower();
            if (KO_ATTR_MULTIPLE_VALUES.Contains(lowerAttr))
            {
                if (multipleValues.ContainsKey(lowerAttr))
                    multipleValues[lowerAttr] = multipleValues[lowerAttr] + ", " + _attributeValue;
                else
                    multipleValues[lowerAttr] = _attributeValue;

                s = " data-bind=\"" + attributeValues;
                
                foreach(string key in multipleValues.Keys)
                {
                    s +=  d + key + ": { " + multipleValues[key] + " }";
                    d = ", ";
                }

                s += "\"";
            }
            else
                s = " data-bind=\"" + attributeValues + d + _attributeValue + "\"";

            return s;
        }

        public virtual bool IsKoAttributeSupported(string attributeName)
        {
            if (_parent != null && (!_parent.IsKoAttributeSupported(attributeName)))
                return false;

            if (_supportedAttributes != null && (!_supportedAttributes.Contains(attributeName)))
                return false;

            return true;
        }

        public virtual bool HasKoElement()
        {
            if(_parent != null)
                return _parent.HasKoElement();
            else
                return false;
        }

        public virtual IKoElementHtmlString<TModel> GetKoElement()
        {
            if (_parent != null)
                return _parent.GetKoElement();
            else
                return null;
        }

        public List<KeyValuePair<string,string>> GetAttributeValues()
        {
            if (_parent != null)
            {
                List<KeyValuePair<string, string>> values = _parent.GetAttributeValues();
                values.Add(new KeyValuePair<string,string>(_attributeName, _attributeValue));

                return values;
            }
            else
                return new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>(_attributeName, _attributeValue) };
        }
    }
}